/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Shader.cpp
*
*	Comments	-	See Shader.h
*
**************************************************************************************/
#include "../Include/Shader.h"
#include "../Include/Engine.h"
#include "../Include/FileSystem.h"
#include "../Include/ResourceCache.h"
#include "../Include/String.h"
#include "../Include/AsyncLoadResource.h"
#include "../Include/ParameterSystem.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/GraphicsPipeline.h"

namespace Pulse
{
	class D3DIncludeCallback : public ID3D10Include
	{
		STDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
		{
			String fileName = String::ToUnicode( pFileName );
			fileName = String(PSX_PATH_EFFECT_TECHNIQUES) + 
				Engine::GetInstance()->GetRenderer()->GetRenderPath()->GetTechniquesPath() +
				String(PSX_PATH_APPEND_SHADERS) + fileName;

			FileHandlePtr hFile = FileSystem::GetInstance()->OpenFile( fileName.GetBuffer() );

			if ( hFile->IsValid() == FALSE )
			{
				PSX_PushError( "Failed to open shader file, %s", fileName.GetBuffer() );
				return EErrorCode::GRAPHICS;
			}

			ResHandlePtr hResource = Engine::GetInstance()->GetResourceCache()->GetHandle( hFile );

			*pBytes = (UINT)hResource->GetSize();
			*ppData = new BYTE[*pBytes];
			PSX_MemCopyPerByte( (void*)*ppData, hResource->GetBuffer(), *pBytes ); 

			return S_OK;
		}

		STDMETHOD(Close)(THIS_ LPCVOID pData)
		{
			delete [] pData;
			return S_OK;
		}

	};

	ShaderDesc::ShaderDesc( void )
	{
		type = EShader::UNKNOWN;
		pMacrolist = PSX_NULL;
	}

	void ShaderDesc::SetAsVertexShader( const CHAR *pFilename, const CHAR *pFunction )		
	{
		SetDescription( EShader::VERTEX, pFilename, pFunction, PSX_String("vs_5_0") );
	}

	void ShaderDesc::SetAsHullShader( const CHAR *pFilename, const CHAR *pFunction )		
	{ 
		SetDescription( EShader::HULL, pFilename, pFunction, PSX_String("hs_5_0") );
	}
	
	void ShaderDesc::SetAsDomainShader( const CHAR *pFilename, const CHAR *pFunction )		
	{ 
		SetDescription( EShader::DOMAIN_, pFilename, pFunction, PSX_String("ds_5_0") );
	}

	void ShaderDesc::SetAsGeometryShader( const CHAR *pFilename, const CHAR *pFunction )	
	{ 
		SetDescription( EShader::GEOMETRY, pFilename, pFunction, PSX_String("gs_5_0") );
	}

	void ShaderDesc::SetAsPixelShader( const CHAR *pFilename, const CHAR *pFunction )		
	{ 
		SetDescription( EShader::PIXEL, pFilename, pFunction, PSX_String("ps_5_0") );
	}

	void ShaderDesc::SetAsComputeShader( const CHAR *pFilename, const CHAR *pFunction )
	{
		SetDescription( EShader::COMPUTE, pFilename, pFunction, PSX_String("cs_5_0") );
	}

	void ShaderDesc::SetDescription( EShader::Type shaderType, const CHAR *pFilename, const CHAR *pFunction, const CHAR *pProfile )
	{
		type = shaderType;
		filename = pFilename;
		function = pFunction;
		profile = pProfile;
	}

	void ShaderDesc::AddMacro( ShaderMacro *pMacro )
	{
		if ( !pMacro )
		{
			PSX_PushWarning( "pMacro is null." );
			return;
		}

		if ( !pMacrolist )
		{
			pMacrolist = new MacroList;
		}

		pMacrolist->PushBack( *pMacro );
	}

	void ShaderDesc::ClearMacro( void )
	{
		if ( pMacrolist )
		{
			PSX_SafeDelete( pMacrolist );
		}
	}

	Shader::Shader( void )
	{
		m_pCompiledShader = PSX_NULL;
	}

	Shader::~Shader( void )
	{

	}

	BOOL Shader::CreateResource( BaseResDesc *pDesc )	
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		m_desc = *((ShaderDesc*)pDesc);

		if ( PerformInitialize(m_desc.IsAsyncLoad()) == EErrorCode::OKAY )
			SetFlagCreated();
		
		return IsCreated();	
	}

	BOOL Shader::DestroyResource( void )				
	{
		Cleanup();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL Shader::LoadResource( void )				
	{ 
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagLoaded();	
			return TRUE;	
		}

		return FALSE;
	}

	BOOL Shader::UnloadResource( void )				
	{
		Cleanup();
		SetFlagUnloaded();
		return FALSE;	
	}

	void Shader::Update( GraphicsPipeline *pPipeline )
	{
		// Set necessary shader resource parameters.
		m_params.BindParameters();

		// This involves a two process update.
		// 1. Check each constant buffer layout if it has changed.
		//	If it changed, then update the constant buffers.

		SIZE_T numBuffers = m_constantBufferLayouts.GetSize();

		for ( SIZE_T i = 0; i < numBuffers; ++i )
		{
			ConstantBufferLayout *pLayout = &m_constantBufferLayouts[i];
			SIZE_T numVariables = pLayout->variables.GetSize();
			BOOL bUpdateBuffer = FALSE;

			for ( SIZE_T j = 0; j < numVariables; ++j )
			{
				ShaderVariable *pShaderVar = &pLayout->variables[j];
				IParameter *pParam = pLayout->m_parameters[j]; 
				
				if ( pShaderVar->m_changeValue != pParam->GetChangeValue() )
				{
					bUpdateBuffer = TRUE;
					break;
				}
			}

			if ( bUpdateBuffer )
			{
				ConstantBuffer *pBuffer = m_constantBuffers[i];
				MappedResource mapRes;
				
				pBuffer->Map( &mapRes );

				for ( SIZE_T j = 0; j < numVariables; ++j )
				{
					ShaderVariable *pShaderVar = &pLayout->variables[j];
					IParameter *pParam = pLayout->m_parameters[j]; 
					
					pShaderVar->m_changeValue = pParam->GetChangeValue();

					if ( pShaderVar->type.classType == EShaderVariableClass::SCALAR )
					{
						void* pData = ((ParameterScalar*)pParam)->GetData();
						PSX_MemCopyPerByte( (void*)(mapRes.pData + pShaderVar->desc.offset), pData, pShaderVar->desc.size );
					}
					else if ( pShaderVar->type.classType == EShaderVariableClass::VECTOR )
					{
						// Vector3 or Vector4
						if ( pShaderVar->type.numColumns == 3 )
						{
							const Vector3 *pParamVec = ((ParameterVector3*)pParam)->GetVector();
							Vector3 *pBufferVec = (Vector3*)&mapRes.pData[pShaderVar->desc.offset];
						
							*pBufferVec = *pParamVec;
						}
						else
						{
							const Vector4 *pParamVec = ((ParameterVector4*)pParam)->GetVector();
							Vector4 *pBufferVec = (Vector4*)&mapRes.pData[pShaderVar->desc.offset];

							*pBufferVec = *pParamVec;
						}
					}
					else if ( pShaderVar->type.classType == EShaderVariableClass::MATRIX_ROWS ||
						pShaderVar->type.classType == EShaderVariableClass::MATRIX_COLUMNS )
					{
						// Is this an array of matrices or not
						if ( pShaderVar->type.numElements == 0 )
						{
							const Matrix4x4 *pMat = ((ParameterMatrix*)pParam)->GetMatrix();
							Matrix4x4 *pBufferMat = (Matrix4x4*)&mapRes.pData[pShaderVar->desc.offset];

							*pBufferMat = *pMat;
						}
						else
						{
							SIZE_T paramSize = ((ParameterMatrixArray*)pParam)->GetNumMatrices() * sizeof(Matrix4x4);
							if (  paramSize ==  pShaderVar->desc.size )
							{
								const Matrix4x4 *pMat = ((ParameterMatrixArray*)pParam)->GetMatrixArray();
								PSX_MemCopyPerByte( (void*)&mapRes.pData[pShaderVar->desc.offset], pMat, paramSize );
							}
							else
							{
								PSX_PushWarning( "Shader variable size and parameter matrix array size mismatch. Skipping this variable update." );
							}
						}
					}
					else
					{
						PSX_PushError( "Unsupported variable class type." );
					}
				}

				pBuffer->UnMap();
			}
		}

		// 2. Bind all shader resources.
		EShader::Type shaderType = GetShaderType();
		for ( SIZE_T i = 0; i < m_boundResources.GetSize(); ++i )
		{
			BoundShaderResourceDesc *pDesc = &m_boundResources[i];

			switch( pDesc->m_type )
			{
			case EShaderInput::CBUFFER:
			case EShaderInput::TBUFFER:
				if ( pDesc->m_pParameterRef->GetType() == EParameter::CONSTANT_BUFFER )
				{
					pPipeline->SetConstantBuffer( shaderType, pDesc->bindSlot, ((ParameterConstantBuffer*)pDesc->m_pParameterRef)->GetConstantBuffer() );
				}
				else
				{
					PSX_PushWarning( "Parameter not a constant buffer or texture buffer." );
				}
				break;
			case EShaderInput::SAMPLER:
				if ( pDesc->m_pParameterRef->GetType() == EParameter::SAMPLER )
				{
					pPipeline->SetSamplerState( shaderType, pDesc->bindSlot, ((ParameterSamplerState*)pDesc->m_pParameterRef)->GetSamplerState() ); 
				}
				else
				{
					PSX_PushWarning( "Parameter not a sampler resource type." );
				}
				break;
			case EShaderInput::TEXTURE:
			case EShaderInput::STRUCTURED:
			case EShaderInput::BYTEADDRESS:
				if ( pDesc->m_pParameterRef->GetType() == EParameter::SHADER_RESOURCE )
				{
					pPipeline->SetShaderResource( shaderType, pDesc->bindSlot, ((ParameterShaderResource*)pDesc->m_pParameterRef)->GetResource() );
				}
				else
				{
					PSX_PushWarning("Parameter not a blabalbala!" );
				}
				break;
			case EShaderInput::UAV_RWTYPED:
				if ( pDesc->m_pParameterRef->GetType() == EParameter::SHADER_RESOURCE )
				{
					pPipeline->SetUnorderedAccessResource( shaderType, pDesc->bindSlot, ((ParameterShaderResource*)pDesc->m_pParameterRef)->GetResource() );
				}
				break;
			default:
				PSX_PushError( "Unknown shader input type. Not yet supported." );
			}

		}
	}

	EErrorCode::Type Shader::CompileShader( void )
	{
		Renderer *pRenderer = Engine::GetInstance()->GetRenderer();
		ID3D11Device *pDevice = (ID3D11Device*)pRenderer->GetNakedDevice();
		ID3DBlob* pErrorMessages = NULL;
		HRESULT hr = 0;

		UINT flags = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;

#ifdef PSX_DEBUG
		flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_WARNINGS_ARE_ERRORS ;
#endif
		String fullPath = String(PSX_PATH_EFFECT_TECHNIQUES) +
			String(pRenderer->GetRenderPath()->GetTechniquesPath()) +
			String(PSX_PATH_APPEND_SHADERS) + m_desc.filename;
		FileHandlePtr hFile = FileSystem::GetInstance()->OpenFile( fullPath.GetBuffer() );

		if ( hFile.IsValid() == FALSE )
		{
			PSX_PushError( "Failed to open shader file, %s", m_desc.filename.GetBuffer() );
			return EErrorCode::GRAPHICS;
		}

		ResHandlePtr hResource = Engine::GetInstance()->GetResourceCache()->GetHandle( hFile );

		Char asciiBuffer1[1024];
		Char asciiBuffer2[1024];
		D3D_SHADER_MACRO *pD3DMacros = PSX_NULL;
		D3DIncludeCallback includeCallback;

		// Check if we have any macros defined
		if ( m_desc.pMacrolist )
		{
			SIZE_T numMacros = m_desc.pMacrolist->GetSize();
			pD3DMacros = new D3D_SHADER_MACRO[numMacros + 1];
		
			for ( SIZE_T i = 0; i < numMacros; ++i )
			{
				ShaderMacro *pMacro = &(*m_desc.pMacrolist)[i];

				SIZE_T nameBufferSize = pMacro->name.GetLength() + 1;
				SIZE_T valueBufferSize = pMacro->value.GetLength() + 1;
				Char *pNameBuffer =  new Char[nameBufferSize];
				Char *pValueBuffer =  new Char[valueBufferSize];

				if ( !pNameBuffer || !pValueBuffer )
				{
					PSX_PushError( "Failed to allocated memory." );
					return EErrorCode::MEMORY;
				}
				
				pD3DMacros[i].Name = pMacro->name.ToMultiByte( pNameBuffer, nameBufferSize );
				pD3DMacros[i].Definition = pMacro->value.ToMultiByte( pValueBuffer, valueBufferSize );
			}

			pD3DMacros[numMacros].Name = PSX_NULL;
			pD3DMacros[numMacros].Definition = PSX_NULL;
		}

		/*if ( FAILED( hr = D3DX11CompileFromMemory( (LPCSTR)hResource->GetBuffer(), hResource->GetSize(),
			String::ToMultiByte( hFile->GetName(), asciiBuffer, 1024 ), PSX_NULL, &includeCallback,
			String::ToMultiByte( m_desc.function.GetBuffer(), asciiBuffer, 1024 ), 
			String::ToMultiByte( m_desc.profile.GetBuffer(), asciiBuffer, 1024 ),
			flags, 0, PSX_NULL, &m_pCompiledShader, &pErrorMessages, PSX_NULL) ) )
		{
			String unicode( (Char*)pErrorMessages->GetBufferPointer() );
			PSX_PushError( "Failed to compile shader \"%s\" with error message: %s", hFile->GetName(), unicode.GetBuffer() );
			pErrorMessages->Release();

			return EErrorCode::GRAPHICS;
		}*/

		if ( FAILED( hr = D3DCompile( (LPCSTR)hResource->GetBuffer(), hResource->GetSize(),
			PSX_NULL, pD3DMacros, &includeCallback,
			String::ToMultiByte( m_desc.function.GetBuffer(), asciiBuffer1, 1024 ), 
			String::ToMultiByte( m_desc.profile.GetBuffer(), asciiBuffer2, 1024 ),
			flags, 0, &m_pCompiledShader, &pErrorMessages ) ) )
		{
			String unicode( (Char*)pErrorMessages->GetBufferPointer() );
			PSX_PushError( "Failed to compile shader \"%s\" with error message: %s", hFile->GetName(), unicode.GetBuffer() );
			pErrorMessages->Release();

			return EErrorCode::GRAPHICS;
		}

		// Free-up allocated memory for d3d shader macros
		if ( pD3DMacros )
		{
			SIZE_T i = 0;

			// Stops if name and or value is null.
			while ( pD3DMacros[i].Name )
			{
				delete [] pD3DMacros[i].Name;
				delete [] pD3DMacros[i].Definition;

				++i;
			}

			delete [] pD3DMacros;
		}

		// We don't need the desc macro list anymore. Remove it.
		m_desc.ClearMacro();

		return EErrorCode::OKAY;
	}

	EErrorCode::Type Shader::QueryParameters( void )
	{
		ID3D11ShaderReflection *pReflector;
		HRESULT hr;

		hr = D3DReflect( m_pCompiledShader->GetBufferPointer(), 
			m_pCompiledShader->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflector );
	
		if ( FAILED( hr ) )
		{
			PSX_PushError( "Failed to query shader parameters." );
			return EErrorCode::GRAPHICS;
		}

		// Get the top level shader information, including the number of constant buffers,
		// as well as the number bound resources (constant buffers + other objects), the
		// number of input elements, and the number of output elements for the shader.
		pReflector->GetDesc( &m_d3dShaderDesc );

		// Get the input and output signature description arrays. These can be used later
		//	for verification of linking shader stages together.
		for ( UINT i = 0; i < m_d3dShaderDesc.InputParameters; ++i )
		{
			D3D11_SIGNATURE_PARAMETER_DESC inputDesc;
			pReflector->GetInputParameterDesc( 0, &inputDesc );
			SignatureParameterDesc inputParam( String::ToUnicode(inputDesc.SemanticName).GetBuffer(), 
				inputDesc.SemanticIndex, inputDesc.Register, DX11Mapping::ToSystemValue(inputDesc.SystemValueType), 
				DX11Mapping::ToRegisterComponent(inputDesc.ComponentType), inputDesc.Mask, inputDesc.ReadWriteMask );
			m_inputSignature.PushBack( inputParam );
		}
		for ( UINT i = 0; i < m_d3dShaderDesc.OutputParameters; ++i )
		{
			D3D11_SIGNATURE_PARAMETER_DESC outputDesc;
			pReflector->GetInputParameterDesc( 0, &outputDesc );
			SignatureParameterDesc outputParam( String::ToUnicode(outputDesc.SemanticName).GetBuffer(), 
				outputDesc.SemanticIndex, outputDesc.Register, DX11Mapping::ToSystemValue(outputDesc.SystemValueType), 
				DX11Mapping::ToRegisterComponent(outputDesc.ComponentType), outputDesc.Mask, outputDesc.ReadWriteMask );
			m_outputSignature.PushBack( outputParam );
		}

		ParameterSystem *pParamSys = Engine::GetInstance()->GetRenderer()->GetParameterSystem();

		// Get the constant buffer information, which will be used for setting parameters
		//	for use by this shader at rendering time.
		for ( UINT i = 0; i < m_d3dShaderDesc.ConstantBuffers; ++i )
		{
			ID3D11ShaderReflectionConstantBuffer *pConstantBuffer = pReflector->GetConstantBufferByIndex( i );
		
			D3D11_SHADER_BUFFER_DESC constantBufferDesc;
			pConstantBuffer->GetDesc( &constantBufferDesc );

			if ( constantBufferDesc.Type == D3D_CT_CBUFFER || constantBufferDesc.Type == D3D_CT_TBUFFER /*||
				constantBufferDesc.Type == D3D_CT_RESOURCE_BIND_INFO*/ ) // Removing RESOURECE_BIND_INFO since it'll be catched in the bound resources (2nd loop below)
			{
				ConstantBufferLayout bufferLayout;
				bufferLayout.desc.Set( String::ToUnicode(constantBufferDesc.Name).GetBuffer(), DX11Mapping::ToShaderBufferType(constantBufferDesc.Type),
					constantBufferDesc.Variables, constantBufferDesc.Size, constantBufferDesc.uFlags );
				
				for ( SIZE_T j = 0; j < constantBufferDesc.Variables; ++j )
				{
					ID3D11ShaderReflectionVariable *pVariable = pConstantBuffer->GetVariableByIndex((UINT)j);
					ShaderVariable shaderVariable;

					// Get variable desc
					D3D11_SHADER_VARIABLE_DESC desc;
					pVariable->GetDesc( &desc );
					shaderVariable.desc.Set( String::ToUnicode(desc.Name).GetBuffer(), desc.StartOffset, desc.Size, 
						desc.uFlags, desc.DefaultValue, desc.StartTexture, desc.TextureSize, desc.StartSampler, desc.SamplerSize );
				
					// Get variable type
					ID3D11ShaderReflectionType *pType = pVariable->GetType();
					D3D11_SHADER_TYPE_DESC typeDesc;
					pType->GetDesc( &typeDesc );
					shaderVariable.type.Set( DX11Mapping::ToShaderVariableClass( typeDesc.Class ), DX11Mapping::ToShaderVariableType(typeDesc.Type),
						typeDesc.Rows, typeDesc.Columns, typeDesc.Elements, typeDesc.Members, typeDesc.Offset, String::ToUnicode(typeDesc.Name).GetBuffer() );
				
					bufferLayout.variables.PushBack(shaderVariable);

					// Get the references to the parameters for binding to these variables.
					IParameter *pParam = PSX_NULL;

					if ( shaderVariable.type.classType == EShaderVariableClass::SCALAR )
					{
						// Currently scalar parameter is of size 4 Bytes. Do a security check here.
						if ( shaderVariable.desc.size == 4 )
						{
							pParam = pParamSys->GetScalar( shaderVariable.desc.name.GetBuffer() );
						}
						else
						{
							PSX_PushError( "Scalar shader variable should be of size 4." );
						}
					}
					else if ( shaderVariable.type.classType == EShaderVariableClass::VECTOR )
					{
						if ( shaderVariable.type.numColumns == 3 )
							pParam = pParamSys->GetVector3( shaderVariable.desc.name.GetBuffer() );
						else if ( shaderVariable.type.numColumns == 4 )
							pParam = pParamSys->GetVector4( shaderVariable.desc.name.GetBuffer() );
						else
						{
							PSX_PushError( "Unsupported vector size. Implement this." );
						}
					}
					else if ( shaderVariable.type.classType == EShaderVariableClass::MATRIX_ROWS ||
						shaderVariable.type.classType == EShaderVariableClass::MATRIX_COLUMNS )
					{
						// 0 if not an array. 1 otherwise.
						UINT numElements = shaderVariable.type.numElements;
						if ( numElements == 0 )
						{
							String &name = shaderVariable.desc.name;

							//// a prefix of gfxRef is a matrix reference. Otherwise just a normal ref
							//POS_T pos = name.FindFirstOf( PSX_String("gfxRef_") );
							//if ( pos == 0 )
							//{
							//	pParam = pParamSys->GetMatrixRef( name.GetBuffer() );
							//}
							//else
							//{
							//	pParam = pParamSys->GetMatrix( name.GetBuffer() );
							//}

							pParam = pParamSys->GetMatrix( name.GetBuffer() );

						}
						else
						{
							pParam = pParamSys->GetMatrixArray( shaderVariable.desc.name.GetBuffer(), numElements );
						}
					}
					else if ( shaderVariable.type.classType == EShaderVariableClass::STRUCT ) // Structured buffer
					{
						// Structured buffer is just a variable. So desc.Variables should have a value of 1
						if ( constantBufferDesc.Variables != 1 )
						{
							PSX_PushError( "StructureBuffer. Constant buffer should have just 1 variable." );
						}

						// Since this one variable IS the name of the buffer change the variable name to it
						shaderVariable.desc.name = constantBufferDesc.Name;

						// Get parameter
						

					}
					else
					{
						PSX_PushError( "Unsupported class type." );
					}

					bufferLayout.m_parameters.PushBack( pParam );
				}

				// Add constant buffer layout
				m_constantBufferLayouts.PushBack( bufferLayout );
			}
		}

		// Get all the resources that is(can be) bound to the shader.
		for ( UINT i = 0; i < m_d3dShaderDesc.BoundResources; ++i )
		{
			D3D11_SHADER_INPUT_BIND_DESC resourceDesc;
			pReflector->GetResourceBindingDesc( i, &resourceDesc );
			BoundShaderResourceDesc boundRes;

			boundRes.m_name = resourceDesc.Name;
			boundRes.m_type = DX11Mapping::ToShaderInput( resourceDesc.Type );
			boundRes.bindSlot = resourceDesc.BindPoint;

			// NOTE: The way we get the parameter ref for constant buffers needs to have a unique name
			//	to avoid clashing with other shaders with the same constant buffer names.
			if ( resourceDesc.Type == D3D_SIT_CBUFFER || resourceDesc.Type == D3D_SIT_TBUFFER )
			{
				boundRes.m_pParameterRef = pParamSys->GetConstantBuffer( boundRes.m_name.GetBuffer() );
			}
			else if ( resourceDesc.Type == D3D_SIT_TEXTURE )
			{
				if ( resourceDesc.Dimension == D3D11_SRV_DIMENSION_TEXTURE2D )
				{
					boundRes.m_pParameterRef = pParamSys->GetShaderResource( boundRes.m_name.GetBuffer(), EShaderResource::TEXTURE2D );
				}
				else if ( resourceDesc.Dimension == D3D11_SRV_DIMENSION_BUFFER )
				{
					boundRes.m_pParameterRef = pParamSys->GetShaderResource( boundRes.m_name.GetBuffer(), EShaderResource::BUFFER );
				}
				else
				{
					PSX_PushError( "Unknown SRV_DIMENSION. Not supported yet." );
				}
			}
			else if ( resourceDesc.Type == D3D_SIT_STRUCTURED )
			{
				boundRes.m_pParameterRef = pParamSys->GetShaderResource( boundRes.m_name.GetBuffer(), EShaderResource::STRUCTURED_BUFFER );
			}
			else if ( resourceDesc.Type == D3D_SIT_SAMPLER )
			{
				boundRes.m_pParameterRef = pParamSys->GetSamplerState( boundRes.m_name.GetBuffer() );
			}
			else if ( resourceDesc.Type == D3D_SIT_UAV_RWTYPED || resourceDesc.Type == D3D_SIT_UAV_RWSTRUCTURED
			|| resourceDesc.Type == D3D_SIT_BYTEADDRESS || resourceDesc.Type == D3D_SIT_UAV_RWBYTEADDRESS
			|| resourceDesc.Type == D3D_SIT_UAV_APPEND_STRUCTURED || resourceDesc.Type == D3D_SIT_UAV_CONSUME_STRUCTURED
			|| resourceDesc.Type == D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER )
			{
				// TODO: Create unordererd access. We may need other types of UAVs?
				if ( resourceDesc.Type == D3D_SIT_UAV_RWTYPED )
				{
					boundRes.m_pParameterRef = pParamSys->GetShaderResource( boundRes.m_name.GetBuffer(), EShaderResource::UNORDERED_ACCESS );
				}
				else
				{
					PSX_PushError( "TODO: Creat unordered access view" );
				}
			}
			else
			{
				PSX_PushError( "Unsupported resource binding type type." );
			}

			m_boundResources.PushBack( boundRes );
		}

		pReflector->Release();

		return EErrorCode::OKAY;
	}

	EErrorCode::Type Shader::CreateConstantBuffers( void )
	{
		ReleaseConstantBuffers();

		ParameterSystem *pParamSys = Engine::GetInstance()->GetRenderer()->GetParameterSystem();
		GraphicsResourceManager *pGM = GraphicsResourceManager::GetInstance();
		SIZE_T numBuffers = m_constantBufferLayouts.GetSize();
		ConstantBufferLayout *pLayout;

		for ( SIZE_T i = 0; i < numBuffers; ++i )
		{
			pLayout = &m_constantBufferLayouts[i];

			if ( pLayout->desc.bufferType == EShaderBufferType::CONSTANT_BUFFER )
			{
				ConstantBufferDesc desc;

				// TODO: Figure out a way to determine if the constant buffer is static
				//	or dynamic. Static constant buffers may not even be needed to be created and
				//	thus saves memory and processing(update) time .
				desc.SetAsDynamicConstantBuffer( pLayout->desc.bufferSize );

				ConstantBuffer *pBuffer = pGM->CreateConstantBuffer( &desc );

				if ( pBuffer == PSX_NULL )
				{
					PSX_PushError( "Failed to create constant buffer." );
					return EErrorCode::GRAPHICS;
				}

				// This assumes that the constant buffer has already been created in the
				//	query parameters function.
				ParameterConstantBuffer *pParam = pParamSys->GetConstantBuffer( pLayout->desc.name.GetBuffer() );
				m_params.AddParameterBinder( pParam )->SetAsConstantBuffer( pBuffer );
				m_constantBuffers.PushBack( pBuffer );
			}
			else if ( pLayout->desc.bufferType == EShaderBufferType::RESOURCE_BIND_INFO )
			{
				// Only supports structured buffer
				if ( pLayout->variables[0].type.classType == EShaderVariableClass::STRUCT )
				{
					// NOTE: Well.. we don't know the array size of the structured buffer so treat this
					//	like the rest of the bound resources...
				}
				else
				{
					PSX_PushError( "Unsupported class type." );
				}
			}
			else
			{

				PSX_PushError( "Finish this." );
			}
		}

		return EErrorCode::OKAY;
	}

	EErrorCode::Type Shader::Initialize( void )
	{
		EErrorCode::Type retCode;

		if ( (retCode = CompileShader()) != EErrorCode::OKAY )
			return retCode;

		EErrorCode::Type ret = OnInitialize();

		if ( ret != EErrorCode::OKAY )
		{
			Cleanup();
			return EErrorCode::GRAPHICS;
		}

		if ( (retCode = QueryParameters()) != EErrorCode::OKAY )
		{
			Cleanup();
			return retCode;
		}

		// Finally, create constant buffers
		if ( CreateConstantBuffers() != EErrorCode::OKAY )
		{
			Cleanup();
			return EErrorCode::_ERROR;
		}

		return EErrorCode::OKAY;
	}

	void Shader::ReleaseConstantBuffers( void )
	{
		for ( SIZE_T i = 0; i < m_constantBuffers.GetSize(); ++i )
		{
			m_constantBuffers[i]->Release();
		}

		m_constantBuffers.Clear();
	}

	void Shader::Cleanup( void )
	{
		OnCleanup();
		ReleaseConstantBuffers();
		PSX_SafeRelease( m_pCompiledShader );
	}
}